<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>RTK平台网页监控版</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        danger: '#EF4444',
                        warning: '#F59E0B',
                        info: '#3B82F6',
                        light: '#F3F4F6',
                        dark: '#1F2937',
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .target-icon {
                position: absolute;
                width: 16px;
                height: 16px;
                border-radius: 50%;
                transform: translate(-50%, -50%);
                box-shadow: 0 0 10px rgba(0,0,0,0.3);
                transition: all 0.2s ease;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                color: white;
                font-size: 10px;
            }
            .target-icon:hover {
                transform: translate(-50%, -50%) scale(1.3);
                z-index: 10;
            }
            .target-icon.selected {
                transform: translate(-50%, -50%) scale(1.5);
                box-shadow: 0 0 15px rgba(0,0,0,0.5);
                border: 2px solid white;
                z-index: 15;
            }
            .tracking-line {
                position: absolute;
                height: 2px;
                transform-origin: left center;
                z-index: 5;
            }
            .info-panel {
                max-height: calc(100vh - 120px);
                overflow-y: auto;
            }
            .axis {
                position: absolute;
                background-color: #3B82F6;
                z-index: 1;
            }
            .axis-label {
                position: absolute;
                font-size: 12px;
                font-weight: bold;
                color: #3B82F6;
                pointer-events: none;
            }
            .axis-tick {
                position: absolute;
                width: 8px;
                height: 1px;
                background-color: #9CA3AF;
                pointer-events: none;
            }
            .axis-tick-y {
                position: absolute;
                width: 1px;
                height: 8px;
                background-color: #9CA3AF;
                pointer-events: none;
            }
            .axis-tick-label {
                position: absolute;
                font-size: 10px;
                color: #6B7280;
                pointer-events: none;
            }
            .select-all-btn {
                margin-bottom: 10px;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-sans text-gray-800 min-h-screen flex flex-col">
    <!-- 顶部按钮栏 -->
    <header class="bg-white shadow-md py-3 px-4 flex flex-wrap justify-between items-center gap-3">
        <h1 class="text-xl font-bold text-gray-800">RTK平台网页监控版</h1>

        <!-- 范围选择控件 -->
        <div class="flex items-center bg-gray-100 rounded-md px-3 py-1.5">
            <label for="rangeSelect" class="text-sm text-gray-600 mr-2">显示范围:</label>
            <select id="rangeSelect" class="bg-transparent text-sm border-none focus:outline-none focus:ring-0">
                <option value="1">1米</option>
                <option value="5">5米</option>
                <option value="10">10米</option>
                <option value="20">20米</option>
                <option value="50" selected>50米</option>
                <option value="100">100米</option>
                <option value="auto">自动适应</option>
            </select>
        </div>

        <div class="flex space-x-3">
            <button id="startRecord" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-md flex items-center transition-all duration-200 transform hover:scale-105">
                <i class="fa fa-play mr-2"></i>开始记录
            </button>
            <button id="clearRecord" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-md flex items-center transition-all duration-200 transform hover:scale-105">
                <i class="fa fa-trash mr-2"></i>清空记录
            </button>
            <button id="saveRecord" class="bg-secondary hover:bg-secondary/90 text-white px-4 py-2 rounded-md flex items-center transition-all duration-200 transform hover:scale-105">
                <i class="fa fa-download mr-2"></i>保存记录
            </button>
        </div>
    </header>

    <!-- 主要内容区域 -->
    <main class="flex-1 flex flex-col md:flex-row p-4 gap-4">
        <!-- 坐标系区域 -->
        <div id="coordinate-container" class="flex-1 bg-white rounded-lg shadow-lg overflow-hidden relative border border-gray-200">
            <div id="grid" class="absolute w-full h-full transition-transform duration-150 origin-center"></div>
            <div id="axes" class="absolute w-full h-full pointer-events-none"></div>
            <div id="targets-container" class="absolute w-full h-full pointer-events-none"></div>
            <div id="tracking-lines-container" class="absolute w-full h-full pointer-events-none"></div>

            <!-- 缩放提示 -->
            <div class="absolute bottom-4 left-4 bg-black/60 text-white text-sm px-3 py-1 rounded-full">
                <i class="fa fa-search-plus mr-1"></i>缩放 | <i class="fa fa-arrows mr-1"></i>平移
            </div>
        </div>

        <!-- 信息面板 -->
        <div class="w-full md:w-80 bg-white rounded-lg shadow-lg border border-gray-200">
            <div class="p-4 border-b border-gray-200">
                <h2 class="text-lg font-semibold">目标信息</h2>
                <div class="mt-2 select-all-btn">
                    <label class="inline-flex items-center cursor-pointer">
                        <input type="checkbox" id="selectAll" class="form-checkbox h-4 w-4 text-primary rounded">
                        <span class="ml-2 text-sm text-gray-700">全选目标</span>
                    </label>
                </div>
            </div>
            <div id="targets-info" class="info-panel p-4 space-y-4">
                <div class="text-center text-gray-500 italic">
                    暂无目标数据
                </div>
            </div>
        </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-white py-3 px-4 border-t border-gray-200 text-center text-sm text-gray-500">
        <p>RTK平台网页监控版 &copy; 2025</p>
    </footer>

    <script>
        // 状态变量
        const state = {
            targets: {}, // 当前目标数据
            tracking: false, // 是否正在记录轨迹
            tracks: {}, // 轨迹数据
            selectedTargets: new Set(), // 选中的目标
            scale: 100, // 缩放比例，1个单位=100像素
            offsetX: 0, // X轴偏移
            offsetY: 0, // Y轴偏移
            isDragging: false, // 是否正在拖动
            lastX: 0, // 上次鼠标X坐标
            lastY: 0, // 上次鼠标Y坐标
            isPinching: false, // 是否正在捏合缩放
            lastDistance: 0, // 上次捏合距离
            pinchCenterX: 0, // 捏合中心X坐标
            pinchCenterY: 0, // 捏合中心Y坐标
            range: 50, // 默认显示50米范围
            autoRange: false, // 是否自动适应范围
        };

        // MODE对应的颜色
        const modeColors = {
            'NON': 'bg-red-500',
            'GPS': 'bg-yellow-500',
            'RTK': 'bg-green-500',
            'default': 'bg-orange-500'
        };

        // DOM元素
        const grid = document.getElementById('grid');
        const axes = document.getElementById('axes');
        const targetsContainer = document.getElementById('targets-container');
        const trackingLinesContainer = document.getElementById('tracking-lines-container');
        const targetsInfo = document.getElementById('targets-info');
        const startRecordBtn = document.getElementById('startRecord');
        const clearRecordBtn = document.getElementById('clearRecord');
        const saveRecordBtn = document.getElementById('saveRecord');
        const coordinateContainer = document.getElementById('coordinate-container');
        const selectAllCheckbox = document.getElementById('selectAll');
        const rangeSelect = document.getElementById('rangeSelect');

        // 初始化
        function init() {
            // 设置网格背景大小
            updateGrid();
            renderAxes();

            // 绑定事件
            bindEvents();

            // 开始轮询数据
            startPolling();
        }

        // 开始轮询数据
        function startPolling() {
            if (state.intervalId) clearInterval(state.intervalId);

            state.intervalId = setInterval(fetchTargets, 300);
            fetchTargets(); // 立即获取一次数据
        }

        // 获取目标数据
        async function fetchTargets() {
            try {
                const response = await fetch('/targets');
                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

                const data = await response.json();
                updateTargets(data.targets);
            } catch (error) {
                console.error('获取目标数据失败:', error);
                // 显示错误信息
                targetsInfo.innerHTML = `
                    <div class="text-center text-red-500">
                        <i class="fa fa-exclamation-triangle mr-2"></i>无法获取目标数据
                    </div>
                `;
            }
        }

        // 更新目标数据
        function updateTargets(newTargets) {
            const updatedTargets = {};

            newTargets.forEach(target => {
                const { IP, MODE, RSSI, LLA, XYZ, DELAY } = target;
                const [longitude, latitude, altitude] = LLA;
                const [x, y, z] = XYZ;

                // 计算距离原点的距离
                const distance = Math.sqrt(x*x + y*y + z*z);

                // 保存目标数据
                updatedTargets[IP] = {
                    ip: IP,
                    mode: MODE,
                    rssi: RSSI,
                    lla: { longitude, latitude, altitude },
                    xyz: { x, y, z },
                    delay: DELAY,
                    distance,
                    timestamp: Date.now()
                };

                // 如果正在记录，添加轨迹点
                if (state.tracking) {
                    if (!state.tracks[IP]) state.tracks[IP] = [];
                    state.tracks[IP].push({
                        x, y, z,
                        rssi: RSSI,
                        mode: MODE,
                        lla: { longitude, latitude, altitude },
                        timestamp: Date.now()
                    });
                }
            });

            state.targets = updatedTargets;

            // 如果是自动模式，调整范围以适应所有目标
            if (state.autoRange && Object.keys(state.targets).length > 0) {
                adjustRangeToFitTargets();
            }

            renderTargets();
            renderTargetsInfo();
            renderTrackingLines();
        }

        // 调整范围以适应所有目标
        function adjustRangeToFitTargets() {
            if (Object.keys(state.targets).length === 0) return;

            // 找到最远的目标
            let maxDistance = 0;
            Object.values(state.targets).forEach(target => {
                maxDistance = Math.max(maxDistance, target.distance);
            });

            // 稍微扩大一点范围以便更好地显示
            const adjustedRange = Math.min(100, Math.max(1, Math.ceil(maxDistance * 1.2)));

            if (adjustedRange !== state.range) {
                state.range = adjustedRange;
                // 更新下拉选择框，但不触发事件
                const event = rangeSelect.onchange;
                rangeSelect.onchange = null;
                rangeSelect.value = adjustedRange;
                rangeSelect.onchange = event;

                // 重新计算缩放比例
                calculateScaleFromRange();
            }
        }

        // 根据选择的范围计算缩放比例
        function calculateScaleFromRange() {
            const containerRect = coordinateContainer.getBoundingClientRect();
            // 计算使所选范围刚好适应容器的缩放比例
            // 留出10%的边距
            const availableWidth = containerRect.width * 0.9;
            const availableHeight = containerRect.height * 0.9;

            // 基于宽度和高度计算合适的缩放比例
            const scaleByWidth = availableWidth / (state.range * 2);
            const scaleByHeight = availableHeight / (state.range * 2);

            // 使用较小的缩放比例以确保所有内容都能显示
            state.scale = Math.min(scaleByWidth, scaleByHeight);

            // 重置偏移量
            state.offsetX = 0;
            state.offsetY = 0;

            updateGrid();
        }

        // 渲染目标点
        function renderTargets() {
            // 清空现有目标点
            targetsContainer.innerHTML = '';

            Object.values(state.targets).forEach(target => {
                const { xyz, mode, ip } = target;
                const { x, y, z } = xyz;

                // 计算在坐标系中的位置
                const containerRect = coordinateContainer.getBoundingClientRect();
                const centerX = containerRect.width / 2;
                const centerY = containerRect.height / 2;

                // 应用缩放和平移
                const scaledX = centerX + x * state.scale + state.offsetX;
                const scaledY = centerY - y * state.scale + state.offsetY; // Y轴向下为负

                // 获取颜色
                const colorClass = modeColors[mode] || modeColors['default'];
                const isSelected = state.selectedTargets.has(ip);

                // 创建目标点元素
                const targetElement = document.createElement('div');
                targetElement.className = `target-icon ${colorClass} ${isSelected ? 'selected' : ''}`;
                targetElement.style.left = `${scaledX}px`;
                targetElement.style.top = `${scaledY}px`;
                targetElement.innerHTML = `<i class="fa fa-dot-circle-o"></i>`;
                targetElement.title = ip;
                targetElement.dataset.ip = ip;

                // 添加点击事件
                targetElement.style.pointerEvents = 'auto';
                targetElement.addEventListener('click', (e) => {
                    e.stopPropagation();
                    toggleTargetSelection(ip);
                });

                targetsContainer.appendChild(targetElement);
            });
        }

        // 切换目标选择状态
        function toggleTargetSelection(ip) {
            if (state.selectedTargets.has(ip)) {
                state.selectedTargets.delete(ip);
            } else {
                state.selectedTargets.add(ip);
            }
            renderTargets();
            renderTargetsInfo();
        }

        // 渲染轨迹线
        function renderTrackingLines() {
            // 清空现有轨迹线
            trackingLinesContainer.innerHTML = '';

            if (!state.tracking) return;

            Object.entries(state.tracks).forEach(([ip, points]) => {
                if (points.length < 2) return;

                const target = state.targets[ip];
                if (!target) return;

                // 获取颜色
                const colorClass = modeColors[target.mode] || modeColors['default'];
                const color = colorClass.split('-')[1]; // 提取颜色名称

                // 计算在坐标系中的位置
                const containerRect = coordinateContainer.getBoundingClientRect();
                const centerX = containerRect.width / 2;
                const centerY = containerRect.height / 2;

                for (let i = 0; i < points.length - 1; i++) {
                    const p1 = points[i];
                    const p2 = points[i + 1];

                    // 应用缩放和平移
                    const x1 = centerX + p1.x * state.scale + state.offsetX;
                    const y1 = centerY - p1.y * state.scale + state.offsetY;
                    const x2 = centerX + p2.x * state.scale + state.offsetX;
                    const y2 = centerY - p2.y * state.scale + state.offsetY;

                    // 计算线段长度和角度
                    const dx = x2 - x1;
                    const dy = y2 - y1;
                    const length = Math.sqrt(dx * dx + dy * dy);
                    const angle = Math.atan2(dy, dx) * 180 / Math.PI;

                    // 创建线段元素
                    const line = document.createElement('div');
                    line.className = `tracking-line bg-${color}-500/50`;
                    line.style.left = `${x1}px`;
                    line.style.top = `${y1}px`;
                    line.style.width = `${length}px`;
                    line.style.transform = `rotate(${angle}deg)`;

                    trackingLinesContainer.appendChild(line);
                }
            });
        }

        // 渲染目标信息
        function renderTargetsInfo() {
            if (Object.keys(state.targets).length === 0) {
                targetsInfo.innerHTML = `
                    <div class="text-center text-gray-500 italic">
                        暂无目标数据
                    </div>
                `;
                return;
            }

            targetsInfo.innerHTML = '';

            Object.values(state.targets).forEach(target => {
                const { ip, mode, rssi, lla, xyz, delay, distance } = target;
                const { longitude, latitude, altitude } = lla;
                const { x, y, z } = xyz;

                // 获取颜色
                const colorClass = modeColors[mode] || modeColors['default'];
                const colorName = colorClass.split('-')[1]; // 提取颜色名称
                const isSelected = state.selectedTargets.has(ip);

                const infoCard = document.createElement('div');
                infoCard.className = `bg-gray-50 p-3 rounded-lg border border-gray-200 hover:border-gray-300 transition-colors duration-200 ${isSelected ? 'border-primary' : ''}`;
                infoCard.innerHTML = `
                    <div class="flex justify-between items-start mb-2">
                        <h3 class="font-semibold text-gray-800">${ip}</h3>
                        <span class="px-2 py-1 rounded-full text-xs font-medium bg-${colorName}-100 text-${colorName}-800">
                            ${mode}
                        </span>
                    </div>
                    <div class="flex items-center mb-2">
                        <label class="inline-flex items-center cursor-pointer">
                            <input type="checkbox" class="form-checkbox h-4 w-4 text-primary rounded target-checkbox" data-ip="${ip}" ${isSelected ? 'checked' : ''}>
                            <span class="ml-2 text-sm text-gray-700">选择记录</span>
                        </label>
                    </div>
                    <div class="grid grid-cols-2 gap-2 text-sm">
                        <div>
                            <span class="text-gray-500"><i class="fa fa-signal mr-1"></i>信号强度:</span>
                            <span class="font-medium">${rssi.toFixed(2)} dBm</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-clock-o mr-1"></i>延迟:</span>
                            <span class="font-medium">${delay.toFixed(2)} s</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-globe mr-1"></i>经度:</span>
                            <span class="font-medium">${longitude.toFixed(6)}°</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-map-marker mr-1"></i>纬度:</span>
                            <span class="font-medium">${latitude.toFixed(6)}°</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-arrows-v mr-1"></i>高度:</span>
                            <span class="font-medium">${altitude.toFixed(2)} m</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-compass mr-1"></i>动向:</span>
                            <span class="font-medium">${x.toFixed(2)} m</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-compass mr-1"></i>北向:</span>
                            <span class="font-medium">${y.toFixed(2)} m</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-rocket mr-1"></i>天向:</span>
                            <span class="font-medium">${z.toFixed(2)} m</span>
                        </div>
                        <div>
                            <span class="text-gray-500"><i class="fa fa-arrows-alt mr-1"></i>距离原点:</span>
                            <span class="font-medium">${distance.toFixed(2)} m</span>
                        </div>
                    </div>
                `;

                // 添加选择事件
                const checkbox = infoCard.querySelector('.target-checkbox');
                checkbox.addEventListener('change', (e) => {
                    if (e.target.checked) {
                        state.selectedTargets.add(ip);
                    } else {
                        state.selectedTargets.delete(ip);
                    }
                    renderTargets();
                });

                targetsInfo.appendChild(infoCard);
            });

            // 更新全选复选框状态
            selectAllCheckbox.checked = Object.keys(state.targets).length > 0 &&
                Object.keys(state.targets).every(ip => state.selectedTargets.has(ip));
        }

        // 更新网格
        function updateGrid() {
            // 由于取消了网格，这里只需要更新原点位置
            renderAxes();
        }

        // 渲染坐标轴和刻度
        function renderAxes() {
            axes.innerHTML = '';

            const containerRect = coordinateContainer.getBoundingClientRect();
            const centerX = containerRect.width / 2;
            const centerY = containerRect.height / 2;

            // X轴
            const xAxis = document.createElement('div');
            xAxis.className = 'axis';
            xAxis.style.width = `${containerRect.width}px`;
            xAxis.style.height = '1px';
            xAxis.style.left = '0';
            xAxis.style.top = `${centerY + state.offsetY}px`;
            axes.appendChild(xAxis);

            // Y轴
            const yAxis = document.createElement('div');
            yAxis.className = 'axis';
            yAxis.style.width = '1px';
            yAxis.style.height = `${containerRect.height}px`;
            yAxis.style.left = `${centerX + state.offsetX}px`;
            yAxis.style.top = '0';
            axes.appendChild(yAxis);

            // X轴标签
            const xLabel = document.createElement('div');
            xLabel.className = 'axis-label';
            xLabel.style.left = `${containerRect.width - 15}px`;
            xLabel.style.top = `${centerY + state.offsetY - 15}px`;
            xLabel.textContent = 'X (东)';
            axes.appendChild(xLabel);

            // Y轴标签
            const yLabel = document.createElement('div');
            yLabel.className = 'axis-label';
            yLabel.style.left = `${centerX + state.offsetX + 5}px`;
            yLabel.style.top = '10px';
            yLabel.textContent = 'Y (北)';
            axes.appendChild(yLabel);

            // 原点图标
            const originIcon = document.createElement('div');
            originIcon.className = 'absolute text-blue-500';
            originIcon.style.left = `${centerX + state.offsetX - 8}px`;
            originIcon.style.top = `${centerY + state.offsetY - 8}px`;
            originIcon.innerHTML = '<i class="fa fa-crosshairs text-lg"></i>';
            axes.appendChild(originIcon);

            // 原点标签
            const originLabel = document.createElement('div');
            originLabel.className = 'axis-label';
            originLabel.style.left = `${centerX + state.offsetX + 15}px`;
            originLabel.style.top = `${centerY + state.offsetY + 5}px`;
            originLabel.textContent = 'O (0,0)';
            axes.appendChild(originLabel);

            // 确定刻度间隔
            let tickInterval;
            if (state.range <= 5) {
                tickInterval = 1; // 1米间隔
            } else if (state.range <= 20) {
                tickInterval = 5; // 5米间隔
            } else if (state.range <= 50) {
                tickInterval = 10; // 10米间隔
            } else {
                tickInterval = 20; // 20米间隔
            }

            // 计算显示的刻度范围
            const visibleRangeX = Math.ceil(containerRect.width / 2 / state.scale);
            const visibleRangeY = Math.ceil(containerRect.height / 2 / state.scale);

            // 添加X轴刻度
            for (let x = -visibleRangeX; x <= visibleRangeX; x += tickInterval) {
                if (x === 0) continue; // 原点已经有标记

                const tickX = centerX + x * state.scale + state.offsetX;

                if (tickX >= 0 && tickX <= containerRect.width) {
                    // 创建刻度线
                    const tick = document.createElement('div');
                    tick.className = 'axis-tick';
                    tick.style.left = `${tickX}px`;
                    tick.style.top = `${centerY + state.offsetY - 4}px`;
                    axes.appendChild(tick);

                    // 创建刻度标签
                    const label = document.createElement('div');
                    label.className = 'axis-tick-label';
                    label.style.left = `${tickX - 5}px`;
                    label.style.top = `${centerY + state.offsetY + 5}px`;
                    label.textContent = `${x}`;
                    axes.appendChild(label);
                }
            }

            // 添加Y轴刻度
            for (let y = -visibleRangeY; y <= visibleRangeY; y += tickInterval) {
                if (y === 0) continue; // 原点已经有标记

                const tickY = centerY - y * state.scale + state.offsetY;

                if (tickY >= 0 && tickY <= containerRect.height) {
                    // 创建刻度线
                    const tick = document.createElement('div');
                    tick.className = 'axis-tick-y';
                    tick.style.left = `${centerX + state.offsetX - 4}px`;
                    tick.style.top = `${tickY}px`;
                    axes.appendChild(tick);

                    // 创建刻度标签
                    const label = document.createElement('div');
                    label.className = 'axis-tick-label';
                    label.style.left = `${centerX + state.offsetX + 5}px`;
                    label.style.top = `${tickY - 5}px`;
                    label.textContent = `${y}`;
                    axes.appendChild(label);
                }
            }
        }

        // 绑定事件
        function bindEvents() {
            // 范围选择事件
            rangeSelect.addEventListener('change', (e) => {
                const value = e.target.value;
                if (value === 'auto') {
                    state.autoRange = true;
                    adjustRangeToFitTargets();
                } else {
                    state.autoRange = false;
                    state.range = parseInt(value);
                    calculateScaleFromRange();
                }
                renderTargets();
                renderTrackingLines();
                renderAxes();
            });

            // 开始记录按钮
            startRecordBtn.addEventListener('click', () => {
                state.tracking = !state.tracking;

                if (state.tracking) {
                    startRecordBtn.innerHTML = '<i class="fa fa-pause mr-2"></i>停止记录';
                    startRecordBtn.classList.remove('bg-primary');
                    startRecordBtn.classList.add('bg-red-500');

                    // 初始化轨迹数据
                    Object.keys(state.targets).forEach(ip => {
                        if (!state.tracks[ip]) state.tracks[ip] = [];
                    });
                } else {
                    startRecordBtn.innerHTML = '<i class="fa fa-play mr-2"></i>开始记录';
                    startRecordBtn.classList.remove('bg-red-500');
                    startRecordBtn.classList.add('bg-primary');
                }
            });

            // 清空记录按钮
            clearRecordBtn.addEventListener('click', () => {
                state.tracks = {};
                renderTrackingLines();
            });

            // 保存记录按钮
            saveRecordBtn.addEventListener('click', saveTracks);

            // 全选复选框
            selectAllCheckbox.addEventListener('change', (e) => {
                if (e.target.checked) {
                    Object.keys(state.targets).forEach(ip => {
                        state.selectedTargets.add(ip);
                    });
                } else {
                    state.selectedTargets.clear();
                }
                renderTargets();
                renderTargetsInfo();
            });

            // 鼠标滚轮缩放
            coordinateContainer.addEventListener('wheel', (e) => {
                e.preventDefault();

                const delta = e.deltaY > 0 ? -0.1 : 0.1;
                const newScale = Math.max(10, Math.min(500, state.scale * (1 + delta))); // 限制缩放范围

                // 计算鼠标位置
                const rect = coordinateContainer.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;

                // 计算鼠标相对于原点的位置
                const mouseRelativeToOriginX = mouseX - (rect.width / 2 + state.offsetX);
                const mouseRelativeToOriginY = mouseY - (rect.height / 2 + state.offsetY);

                // 应用新缩放比例
                state.scale = newScale;

                // 调整偏移量，使鼠标指向的点保持不变
                state.offsetX = mouseX - (rect.width / 2) - mouseRelativeToOriginX * (state.scale / (state.scale / (1 + delta)));
                state.offsetY = mouseY - (rect.height / 2) - mouseRelativeToOriginY * (state.scale / (state.scale / (1 + delta)));

                updateGrid();
                renderTargets();
                renderTrackingLines();
            });

            // 鼠标拖动
            coordinateContainer.addEventListener('mousedown', (e) => {
                if (e.button !== 0) return; // 只处理左键

                state.isDragging = true;
                state.lastX = e.clientX;
                state.lastY = e.clientY;
                coordinateContainer.style.cursor = 'grabbing';
            });

            document.addEventListener('mousemove', (e) => {
                if (!state.isDragging) return;

                const dx = e.clientX - state.lastX;
                const dy = e.clientY - state.lastY;

                state.offsetX += dx;
                state.offsetY += dy;

                state.lastX = e.clientX;
                state.lastY = e.clientY;

                updateGrid();
                renderTargets();
                renderTrackingLines();
            });

            document.addEventListener('mouseup', () => {
                state.isDragging = false;
                coordinateContainer.style.cursor = 'default';
            });

            // 触摸事件 - 单指拖动
            coordinateContainer.addEventListener('touchstart', (e) => {
                if (e.touches.length === 1) {
                    state.isDragging = true;
                    state.lastX = e.touches[0].clientX;
                    state.lastY = e.touches[0].clientY;
                    coordinateContainer.style.cursor = 'grabbing';
                } else if (e.touches.length === 2) {
                    // 双指捏合缩放
                    state.isPinching = true;
                    const touch1 = e.touches[0];
                    const touch2 = e.touches[1];

                    // 计算两指距离
                    const dx = touch2.clientX - touch1.clientX;
                    const dy = touch2.clientY - touch1.clientY;
                    state.lastDistance = Math.sqrt(dx * dx + dy * dy);

                    // 计算捏合中心
                    state.pinchCenterX = (touch1.clientX + touch2.clientX) / 2;
                    state.pinchCenterY = (touch1.clientY + touch2.clientY) / 2;
                }
            });

            coordinateContainer.addEventListener('touchmove', (e) => {
                e.preventDefault();

                if (state.isDragging && e.touches.length === 1) {
                    const touch = e.touches[0];
                    const dx = touch.clientX - state.lastX;
                    const dy = touch.clientY - state.lastY;

                    state.offsetX += dx;
                    state.offsetY += dy;

                    state.lastX = touch.clientX;
                    state.lastY = touch.clientY;

                    updateGrid();
                    renderTargets();
                    renderTrackingLines();
                } else if (state.isPinching && e.touches.length === 2) {
                    const touch1 = e.touches[0];
                    const touch2 = e.touches[1];

                    // 计算两指新距离
                    const dx = touch2.clientX - touch1.clientX;
                    const dy = touch2.clientY - touch1.clientY;
                    const newDistance = Math.sqrt(dx * dx + dy * dy);

                    // 计算缩放比例
                    const scaleFactor = newDistance / state.lastDistance;
                    const newScale = Math.max(10, Math.min(500, state.scale * scaleFactor));

                    // 计算捏合中心
                    const newPinchCenterX = (touch1.clientX + touch2.clientX) / 2;
                    const newPinchCenterY = (touch1.clientY + touch2.clientY) / 2;

                    // 计算鼠标相对于原点的位置
                    const rect = coordinateContainer.getBoundingClientRect();
                    const pinchRelativeToOriginX = state.pinchCenterX - (rect.width / 2 + state.offsetX);
                    const pinchRelativeToOriginY = state.pinchCenterY - (rect.height / 2 + state.offsetY);

                    // 应用新缩放比例
                    state.scale = newScale;

                    // 调整偏移量，使捏合中心保持不变
                    state.offsetX = state.pinchCenterX - (rect.width / 2) - pinchRelativeToOriginX * state.scale / (state.scale / scaleFactor);
                    state.offsetY = state.pinchCenterY - (rect.height / 2) - pinchRelativeToOriginY * state.scale / (state.scale / scaleFactor);

                    // 更新捏合状态
                    state.lastDistance = newDistance;
                    state.pinchCenterX = newPinchCenterX;
                    state.pinchCenterY = newPinchCenterY;

                    updateGrid();
                    renderTargets();
                    renderTrackingLines();
                }
            });

            coordinateContainer.addEventListener('touchend', (e) => {
                state.isDragging = false;
                state.isPinching = false;
                coordinateContainer.style.cursor = 'default';
            });

            // 窗口大小变化
            window.addEventListener('resize', () => {
                if (!state.isDragging && !state.isPinching) {
                    // 重新计算适合当前窗口大小的缩放比例
                    calculateScaleFromRange();
                }
                renderTargets();
                renderTrackingLines();
                renderAxes();
            });
        }

        // 保存轨迹数据
        function saveTracks() {
            const selectedTracks = Array.from(state.selectedTargets);

            if (selectedTracks.length === 0) {
                alert('请先选择要保存的目标');
                return;
            }

            const hasData = selectedTracks.some(ip => state.tracks[ip] && state.tracks[ip].length > 0);

            if (!hasData) {
                alert('选中的目标没有轨迹数据可保存');
                return;
            }

            // 构建CSV内容
            let csvContent = "IP,Timestamp,RSSI,Mode,East(North),North(Y),Up(Z),Longitude,Latitude,Altitude\n";

            selectedTracks.forEach(ip => {
                const points = state.tracks[ip] || [];
                points.forEach(point => {
                    const timestamp = new Date(point.timestamp).toISOString();
                    const { x, y, z, rssi, mode, lla } = point;
                    const { longitude, latitude, altitude } = lla;

                    csvContent += `${ip},${timestamp},${rssi.toFixed(2)},${mode},${x.toFixed(2)},${y.toFixed(2)},${z.toFixed(2)},${longitude.toFixed(6)},${latitude.toFixed(6)},${altitude.toFixed(2)}\n`;
                });
            });

            // 创建下载链接
            const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
            const url = URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.setAttribute('href', url);
            link.setAttribute('download', `target_tracks_${new Date().toISOString().slice(0,19).replace(/:/g,'-')}.csv`);
            link.style.visibility = 'hidden';

            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        // 启动应用
        document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>